﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using System.Collections.Generic;
using LastStand.gameContent.Friendlies;
using System.Linq;

namespace LastStand.gameContent
{
    public class ViewControl : Sprite
    {
        /*\ 
         * ViewControl will be where the Background texture is drawn.
         * Every class with a postion, must allow this class to see and
         * alter that position so every texture can move in unison
         * based on this classes updates. 
        \*/

        //selectedSprite represents the sprite in focus for the build phase
        Sprite selectedSprite;
        Grid grid;

		//our preferred number of grid lines
		private const int HGRIDLINES = 12;
		private const int VGRIDLINES = 20;

		//to hold the length and width of each grid box
		private int gridSpacing;

        List<float> gridXSpacing;
        List<float> gridYSpacing;
        public List<Line> lineList;

        public ViewControl(Vector2 spawn, Texture2D texture, Grid grid)
        {
            //games grid passed to this class
            this.grid = grid;

            //what gestures are enabled
            TouchPanel.EnabledGestures = GestureType.Flick | GestureType.Pinch | GestureType.FreeDrag;
            
			//the camera will start at the players home every time the game is started.
			this.Position = spawn;
            
			//textures and scale
            this.Texture = texture;
            this.Scale = 1.0f;

			//find the spacing for grid lines based on screen size divided by preferred
			//number of grid lines on the x-axis. Only need one gridSpacing variable because
			//grids are square.
			this.gridSpacing = GraphicsDeviceManager.DefaultBackBufferWidth / VGRIDLINES;

			//initialize line list
			this.lineList = new List<Line>();

			//SpacingFlipper tracks when we've finished
			//the horizontal grid lines so we can switch to drawing
			//the vertical grid lines
			int SpacingFlipper = GraphicsDeviceManager.DefaultBackBufferHeight; 

			//draws the horizontal and vertical lines
			for (int space = 0; space <= SpacingFlipper; space += this.gridSpacing)
			{
				Line line = new Line();
				line.Stroke = new SolidColorBrush(Colors.Red);
				line.StrokeThickness = 2;

				//draw from left to right
				line.X1 = 0;
				line.Y1 = space;

				//end at the side of the screen
				line.X2 = GraphicsDeviceManager.DefaultBackBufferWidth;
				line.Y2 = space;

				grid.Children.Add(line);
				lineList.Add(line);
			}

			//change our spacing 
			SpacingFlipper = GraphicsDeviceManager.DefaultBackBufferWidth;

			for (int space = 0; space <= this.Texture.Width; space += this.gridSpacing)
			{
				Line line = new Line();
				line.Stroke = new SolidColorBrush(Colors.Red);
				line.StrokeThickness = 2;

				//these lines always start from y = 0 and at x = space
				line.X1 = space;
				line.Y1 = 0;

				//they end at the top of the screen at y = screenheight
				//and x = space
				line.X2 = space;
				line.Y2 = GraphicsDeviceManager.DefaultBackBufferHeight;

				grid.Children.Add(line);
				lineList.Add(line);
			}
/*
            this.gridXSpacing = new List<float>();
            this.gridYSpacing = new List<float>();


            // find grid lines across the width of the screen
            for (int i = 0;
                i <= this.Texture.Width;
                i += 60)
            {
                gridXSpacing.Add(i);
            }

            for (int i = 0;
                i <= this.Texture.Height;
                i += 80)
            {
                gridYSpacing.Add(i);
            }

            foreach (int x in gridXSpacing)
            {
                Line line = new Line();
                line.Stroke = new SolidColorBrush(Colors.Red);
                line.StrokeThickness = 2;

                line.X1 = x;
                line.Y1 = 0;
                line.X2 = x;
                line.Y2 = this.Texture.Height;

                grid.Children.Add(line);
                lineList.Add(line);
            }
            foreach (int y in gridYSpacing)
            {
                Line line = new Line();
                line.Stroke = new SolidColorBrush(Colors.Red);
                line.StrokeThickness = 2;

                line.X1 = 0;
                line.Y1 = y;
                line.X2 = this.Texture.Width;
                line.Y2 = y;

                grid.Children.Add(line);
                //lineList.Add(line);
            }
 */
        }

        public void Update(GameTimerEventArgs timer, List<Sprite> spriteList)
        {
			int vertLineNum = 0;
			for (int i = 0; i < lineList.Count; i++)
			{
				if (lineList[i].Y1 == 0)
				{
					lineList[i].X1 = (float)Math.Round(this.GetPosition.X + (gridSpacing * vertLineNum), 2);
					lineList[i].X2 = (float)Math.Round(this.GetPosition.X + (gridSpacing * vertLineNum), 2);
					vertLineNum++;
				}
			}

            //handles all gestures
            HandleTouch(spriteList);

            //updates the backgrounds position based on a velocity which degrades over time..
            //then does the same for all sprites and their velocity.
            VelocityDegradation(spriteList);

            //creates a relativity between each sprite and the boundries of the map
            //this allows us to check for collision between the edge of the map and the sprite
            foreach (Sprite sprite in spriteList)
            {
                sprite.OriginPos = sprite.GetPosition - this.GetPosition;
            }

            //makes the highest position.y value object appear on the highest view level
            StructDepth(spriteList);

            //restricts the camera from moving out of bounds
            ViewRestriction(spriteList);
        }

        public void VelocityDegradation(List<Sprite> spriteList)
        {
			foreach (Sprite sprite in spriteList)
			{
				sprite.VelocityX = (sprite.GetVelocity.X * .9f);
			}
            this.VelocityX = (float)Math.Round((this.GetVelocity.X * .9f), 2);

            foreach (Sprite sprite in spriteList)
            {
                sprite.PositionX = (sprite.GetPosition.X + (sprite.GetVelocity.X * .01f));
			}
            this.PositionX = (float)Math.Round((this.GetPosition.X + (this.GetVelocity.X * .01f)), 2);

        }

        //handles basic gestures that will alter the players view
        public void HandleTouch(List<Sprite> spriteList)
        {
            //create a list of all player touches
            TouchCollection touches = TouchPanel.GetState();

            //if the touch list is not empty
            if (touches.Count > 0 && touches[0].State == TouchLocationState.Pressed)
            {
                //find point of the first touch
                Microsoft.Xna.Framework.Point touchPoint = new Microsoft.Xna.Framework.Point((int)touches[0].Position.X, (int)touches[0].Position.Y);

                //when there is a new touch, unfocus from foccused sprite
                selectedSprite = null;

                foreach (Sprite checkSprite in spriteList)
                {
                    //if the players touch falls in a sprites swollen hitbox
                    //and that sprite isnt constructed yet...
                    if (checkSprite.HitBounds.Contains(touchPoint) && !checkSprite.isConstructed)
                    {
                        //that sprite becomes focused sprite
                        selectedSprite = checkSprite;
                    }
                }

            }

            //while touches are available...
            while (TouchPanel.IsGestureAvailable)
            {
                //create an object to detect the type of gesture
                GestureSample gest = TouchPanel.ReadGesture();

                //find what gesture is made..
                switch (gest.GestureType)
                {
                    //if the gesture is a flick...
                    case GestureType.Flick:
                    {
                        //if not sprite is in focus
                        if (selectedSprite == null)
                        {
                            //set the velocity of background and all sprites
                            this.VelocityX = (float)Math.Round(gest.Delta.X, 2);
                            foreach (Sprite sprite in spriteList)
                            {
                                sprite.VelocityX = gest.Delta.X;
                            }
                        }
                    }
					break;

                    //if the gest is free drag
                    case GestureType.FreeDrag:
                    {
                        //if there is no sprite in focus
                        if (selectedSprite == null)
                        {
                            //changes the position of the background and all sprites
                            //equal to the change of the finger position of the screen
                            this.PositionX = (float)Math.Round((float)this.GetPosition.X + (float)gest.Delta.X, 2);
                            foreach (Sprite sprite in spriteList)
                            {
                                sprite.PositionX = (float)Math.Round((float)sprite.GetPosition.X + (float)gest.Delta.X, 2);
                            }
                        }
                        else
                        {
                            //only the focused sprite is dragged by the player
                            //selectedSprite.Position = selectedSprite.GetPosition + gest.Delta;

                            for (int i = 0; i < 11; i++)
                            {
                                if (gest.Position.Y > lineList[i].Y1 && gest.Position.Y < lineList[i + 1].Y1)
                                    selectedSprite.PositionY = (float)(lineList[i].Y1 + lineList[i + 1].Y1) / 2 - selectedSprite.GetCenter.Y;
                            }
                            for (int i = 12; i < 31; i++)
                            {
                                if (gest.Position.X> lineList[i].X1 && gest.Position.X < lineList[i + 1].X1)
                                    selectedSprite.PositionX = (float)(lineList[i].X1 + lineList[i + 1].X1) / 2 - selectedSprite.GetCenter.X;
                            }
                        }
                    }
                    break;

                   /* //if the guest is multitouch zoom in...
                    case GestureType.Pinch:
                        {
                            // get the current and previous locations of the two fingers
                            Vector2 a = gest.Position;
                            Vector2 aOld = gest.Position - gest.Delta;
                            Vector2 b = gest.Position2;
                            Vector2 bOld = gest.Position2 - gest.Delta2;

                            // figure out the distance between the current and previous locations
                            float d = Vector2.Distance(a, b);
                            float dOld = Vector2.Distance(aOld, bOld);

                            // calculate the difference between the two and use that to alter the scale
                            float scaleChange = (d - dOld) * .01f;
                            this.Scale = this.GetScale + scaleChange;
                            foreach (Sprite sprite in spriteList)
                            {
                                sprite.Scale = sprite.GetScale + scaleChange;
                            }
                        }
                        break; */
                }
            }

            if (touches.Count == 0)
            {
                selectedSprite = null;
            }
        }

        public override void Draw(SpriteBatch sb)
        {
            sb.Draw(this.Texture,
                this.GetPosition,
                null,
                Microsoft.Xna.Framework.Color.White,
                0.0f,
                Vector2.Zero,
                this.GetScale,
                SpriteEffects.None,
                0);
        }

        public void ViewRestriction(List<Sprite> spriteList)
        {
            //if the background reaches the games left bounds....
            if (this.GetPosition.X > 0)
            {
                //set the background to that bounds
                this.PositionX = 0;
                this.VelocityX = 0;

                foreach (Sprite sprite in spriteList)
                {
                    sprite.Position = this.GetPosition + sprite.GetOriginPos;
                    sprite.VelocityX = 0;
                }
            }

            //if the background reaches the games right bounds....
            if (this.GetPosition.X < -(this.Texture.Width - GraphicsDeviceManager.DefaultBackBufferWidth) - 1)
            {
                //set the background to that bounds
                this.PositionX = -(this.Texture.Width - GraphicsDeviceManager.DefaultBackBufferWidth);
                this.VelocityX = 0;

                foreach (Sprite sprite in spriteList)
                {
                    sprite.PositionX = (int)sprite.GetOriginPos.X - (this.Texture.Width - GraphicsDeviceManager.DefaultBackBufferWidth);
                    sprite.VelocityX = 0;
                }
            }

            //maps y is always set to 0
            this.PositionY = 0;
        }

        //aranges sprites in a matter that makes sense from the POV
        public void StructDepth(List<Sprite> spriteList)
        {
            for (int i = 0; i < spriteList.Count; i++)
            {
                for (int c = 0; c < spriteList.Count - i - 1; c++)
                {
                    //if a sprite in the list has a greater y position than the sprite infront of it
                    //trades their spots in the list.
                    if (spriteList[c].GetPosition.Y > spriteList[c + 1].GetPosition.Y)
                    {
                        Sprite temp = spriteList[c];
                        spriteList[c] = spriteList[c + 1];
                        spriteList[c + 1] = temp;
                    }
                }
            }
        }
    }
}
